var G;

window.onload = () => {
    [w, h] = [window.innerWidth, window.innerHeight];
    [map.width, map.height] = [w, h];

    var mousex = mousey = 0;

    // 画布环境初始化
    const draw = map.getContext("2d");
    draw.textBaseline = "middle";

    // 按照可视区域宽度百分之一作为标准单位
    const vw = n => w / 100.0 * n + .5;

    // 按照可视区域高度百分之一作为标准单位
    const vh = n => h / 100.0 * n + .5;

    // 按照可视区域宽高中最小的百分之一作为标准单位
    const vmin = n => (w >= h ? h : w) / 100.0 * n + .5;

    // 按照可视区域宽高中最小的百分之一的黄金比例作为标准单位
    const vm = n => vmin(n * 0.618);

    // 获取矩形
    const rectD = (x, y, w, h) => [x - w / 2, y - h / 2, w, h];

    // 获取矩形对角
    const rectF = (x, y, w, h) => [x - w / 2, y - h / 2, x + w / 2, y + h / 2];

    // 获取两点间距离
    const getDist = (a, b) =>
        Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));

    // 返回弧度角度
    const deg2 = a => Math.PI / 180 * (a % 360);

    // 凭借三点获取两边夹角
    const deg3 = (a, b, c) => {
        const [AB, AC, BC] = [getDist(a, b), getDist(a, c), getDist(b, c)];
        const cosA = (Math.pow(AB, 2) + Math.pow(AC, 2) -
            Math.pow(BC, 2)) / (2 * AB * AC);
        const angleA = Math.round(deg2(Math.acos(cosA)));

        return angleA;
    }

    // 获取给定范围内的随机值
    const random = (a, b) => Math.round(Math.random() * (b - a)) + a;

    // 概率事件
    const PR = p => {
        if (p == 0) return false;
        if (p == 100) return true;
        if (p == 50) return random(0, 100) % 2 == 0;
        if (p > 50) return random(0, 100) %
            Math.floor(100 / (100 - p)) !== 0;
        if (p < 50) return random(0, 100) %
            Math.floor(100 / p) === 0;
    }

    // 优化的数组索引遍历
    const foreach = (arr, callback) => {
        var i = 0;
        var l = arr.length;
        while (i < arr.length) {
            if (!!callback(arr[i], i)) break;
            i += 1 - (l - arr.length);
            l = arr.length;
        }
    }

    // 获取当前时间戳
    const dtr = () => new Date().getTime();
    var dtb = dtr();
    var dtt = 0;
    var dtl = 0;
    const dt = () => dtt - dtb;

    // 游戏核心数据与实现
    const Game = G = (() => {
        // 实体基础类
        const Entity = function (x, y, w, h) {
            this.type = "Entity"; // 实体类标识
            this.x = x; // 中心x坐标
            this.y = y; // 中心y坐标
            this.w = w; // 宽
            this.h = h === undefined ? w : h; // 高
            this.w2 = this.w / 2; // 为简化代码设置
            this.h2 = this.h / 2; // 为简化代码设置
            this.noBounce = false; // 不触发反弹事件
            this.color = "#000"; // 颜色
            this.color2 = "#fff"; // 渐变背色
            this.opacity = 1.0; // 透明度
            this.txt = ""; // 显示文本
            this.htxt = ""; // 显示汉字
            this.fontSize = vm(4); // 显示文本的文字大小
            this.txtColor = "#fff"; // 显示文本的文字颜色
            this.score = 0; // 价值分数

            // 获取矩形信息
            this.rect = () => rectD(this.x, this.y, this.w, this.h);

            // 获取矩形对角信息
            this.vector = () => rectF(this.x, this.y, this.w, this.h);

            // 移动实体坐标
            this.move = (x, y) => {
                if (x !== undefined && !isNaN(x)) this.x = x;
                if (y !== undefined && !isNaN(y)) this.y = y;
            }

            // 显示条件
            this.visible = () => true;
        }

        // 方块类
        const Block = function (x, y, w, h, hth, nb, color) {
            Entity.call(this, ...arguments); // 继承实体类
            this.type = "Block";
            this.color = color; // 方块颜色
            this.health = hth; // 方块可受击次数
            this.newBullet = nb; // 被动小球分裂数
            this.effectBoom = false; // 爆发效果

            // 受击事件
            this.hit = (index, bullet) => {
                // 方块颜色变化
                if (this.newBullet > 0) { }
                else if (this.health !== -1)
                    this.color = cfg.c.bd;

                // 永驻方块判定
                if (this.health > 0) {
                    this.health--;
                    this.txt = "" + this.health;
                    // 被撞后加分
                    Game.score += this.score;
                }

                // 移除破坏了的方块
                if (this.health === 0) Game.blocks.splice(index, 1);

                // 被动小球分裂逻辑
                if (this.newBullet > 0) {
                    this.txt = this.health + " x" + this.newBullet;

                    // 分裂小球不允许再次分裂
                    if (bullet.ia)
                        return;

                    for (let i = 0; i < this.newBullet; i++) {
                        const d = bullet.deg + random(-cfg.btnbd, cfg.btnbd),
                            b = Game.addBullet(bullet.x, bullet.y, d, bullet.speed);
                        b.ia = true;
                        b.addSpeed = cfg.btnbas;
                        b.color = cfg.c.btnb;
                    }
                }

                // 爆发效果事件
                if (this.effectBoom) {
                    // 分裂小球不允许再次触发
                    if (bullet.ia)
                        return;

                    Game.Launcher.effectBoomT = dt() + cfg.ebt;
                }
            }
        }

        // 小球类
        const Bullet = function (x, y, r, deg, speed) {
            Entity.call(this, x, y, r, r); // 继承实体类
            this.type = "Bullet";
            this.r = r;
            this.color = cfg.c.bt; // 小球颜色
            this.color2 = cfg.c.bt2; // 小球背色
            this.deg = deg; // 方向角度
            this.speed = speed; // 初始速度
            this.addSpeed = 1; // 速度倍率
            this.st = dt(); // 位移发生时间
            this.lx = x; // 上一次X
            this.ly = y; // 上一次Y
            this.ia = false; // 为分裂小球标识

            // 反弹实现（计算反弹角度）
            var bounceImpl = (block, isC, isR) => {
                // 使逻辑化简
                if (this.deg > 180) this.deg -= 360;
                if (this.deg < -180) this.deg += 360;

                // 左右反弹
                if (this.o == 1) {
                    if (isC) {
                        isC();
                    } else {
                        this.deg = this.deg * -1;
                    }
                }

                // 上下反弹
                if (this.o == 0) {
                    if (isR) {
                        isR();
                    } else {
                        if (this.deg < 0 && this.deg > -180)
                            this.deg = -(this.deg + 180)
                        if (this.deg < 180 && this.deg > 0)
                            this.deg = -(this.deg - 180)
                    }
                }

                this.deg += 180;
            }

            // 反弹事件
            const event = [
                // 与发射平台发生反弹
                [Game.Launcher, launcher => {
                    bounceImpl(launcher, null, isR = () => {
                        const o = this.x - launcher.x;
                        this.deg = o / launcher.w * 90 + 90;
                    });
                }],
                // 与障碍物发生反弹
                [Game.blocks, (block, i) => {
                    // 触发方块被动更新
                    block.hit(i, this);

                    // 无尽模式普通小球超速穿透
                    if (Game.infinity) {
                        if (!this.ia && this.addSpeed > cfg.btiasmax * 0.8)
                            return;
                    }

                    bounceImpl(block);
                }],
                // 小球相互反弹
                [Game.bullets, bullet => {
                    bounceImpl(bullet);
                }],
                // 杂项
                [Game.common, common => {
                    bounceImpl(common);
                }]
            ],
                // 反弹实现（检测碰撞）
                call = (entity, todo, i) => {
                    // 判断是否参与反弹
                    if ([
                        entity.noBounce,
                        this == entity, // 跳过本身
                        Game.infinity && entity.type == "Bullet", // 无尽模式小球
                    ].findIndex(e => !!e) > -1)
                        return;

                    // 反弹检测
                    const p2 = (o, n, p) => {
                        return (n - o) * p + o;
                    }
                    const p3 = (o, e, n) => {
                        return (e - o) / (n - o);
                    }
                    const e0 = (o, e, n, b2, e2) => {
                        return o - b2 >= e + e2 && n - b2 <= e + e2
                    }
                    const e1 = (o, e, n, b2, e2) => {
                        return o + b2 <= e - e2 && n + b2 >= e - e2;
                    }

                    const cr = [], d = [
                        [this.lx, entity.x, this.x, this.w2, entity.w2],
                        [this.ly, entity.y, this.y, this.h2, entity.h2]
                    ];

                    // 生成每个边界的越界情况
                    d.map(v => cr.push(e0(...v), e1(...v)));

                    // 四个方向是否发生越界
                    const vi = cr.findIndex(e => !!e);
                    if (vi === -1) return;

                    // 判断方向
                    const xor = vi % 2 == 0 ? 1 : -1;
                    this.o = vi > 1 ? 0 : 1;

                    // 挡住并尝试识别碰撞
                    var bx, by, at;
                    if (this.o == 0) {
                        by = entity.y + (entity.h2 + this.h2) * xor;
                        bx = p2(this.lx, this.x, p3(this.ly, by, this.y));
                        if (
                            bx + this.w2 >= entity.x - entity.w2 &&
                            bx - this.w2 <= entity.x + entity.w2
                        ) at = true;
                    } else {
                        bx = entity.x + (entity.w2 + this.w2) * xor;
                        by = p2(this.ly, this.y, p3(this.lx, bx, this.x));
                        if (
                            by + this.h2 >= entity.y - entity.h2 &&
                            by - this.h2 <= entity.y + entity.h2
                        ) at = true;
                    }

                    // 发生碰撞后的事件
                    if (!at && !Frontier.rectWith(this, entity)) return;
                    // 位置矫正
                    this.move(bx, by);
                    todo(entity, i);

                    return true;
                }

            this.bounce = () => {
                var happened = false;

                // 遍历所有实体查找碰撞体并制造碰撞事件
                foreach(event, ([obj, todo]) => {
                    if (obj instanceof Array)
                        foreach(obj, (entity, i) => {
                            return happened = call(entity, todo, i);
                        });
                    else call(obj, todo);
                    return happened;
                });

                return happened;
            }

            this.update = () => {
                if (this.speed == 0) return;
                const d = (dt() - this.st) / 1E3 * this.speed * this.addSpeed;
                const r = deg2(this.deg);
                const nx = this.x + Math.cos(r) * d;
                const ny = this.y + Math.sin(r) * d;
                [this.lx, this.ly] = [this.x, this.y];
                this.move(nx, ny);
                this.st = dt();

                // 小球颜色随速度变化而变化
                if (!this.ia) {
                    if (Game.infinity) {
                        if (this.addSpeed > cfg.btiasmax * 0.8)
                            this.color = cfg.c.btias;
                    }
                    else {
                        if (this.addSpeed > cfg.btasmax * 0.9)
                            this.color2 = cfg.c.btas2;
                    }
                }

                // 发生碰撞
                if (this.bounce()) {
                    // 当碰撞发生后的事件
                    // 小球加速度
                    if (Game.infinity) {
                        if (this.addSpeed < cfg.btiasmax)
                            this.addSpeed *= cfg.btiasa;
                        else this.addSpeed = cfg.btiasmax;
                    } else {
                        if (this.addSpeed < cfg.btasmax)
                            this.addSpeed *= cfg.btasa;
                        else this.addSpeed = cfg.btasmax;
                    }
                }
            }
        }

        const game = {
            // 所有障碍方块
            blocks: [],
            // 所有小球
            bullets: [],
            // 杂项
            common: [],
            // 无尽模式标识
            infinity: false,
            // 无尽模式下落时间戳
            infinity_dt: dt(),
            // 游戏暂停标识
            paused: false,
            // 游戏分数
            score: 0,
        };

        [game.Block, game.Bullet] = [Block, Bullet];

        game.addBullet = (x, y, deg, speed) => {
            const bullet = new Bullet(x, y, vm(4), deg, speed);
            game.bullets.push(bullet);
            return bullet;
        }

        game.addBlock = (x, y, w, h, hth, nb, color) => {
            const block = new Block(x, y, w, h, hth, nb, color || cfg.c.b);
            if (Game.infinity) game.blocks.push(block);
            else game.blocks.unshift(block);
            return block;
        }

        game.addCBlock = (x, y, w, h, color) => {
            const block = new Block(x, y, w, h, -1, 0, color);
            block.type = "Common";
            block.noBounce = true;
            block.update = () => { };
            game.common.push(block);
            return block;
        }

        // 不触发反弹的实体不计入总数
        // 永驻方块不计入方块总数
        game.getBlockCount = () => Game.blocks.filter(e =>
            e.health !== -1 && !e.noBounce).length;

        game.getBulletCount = () => Game.bullets.filter(e => !e.noBounce).length;
        return game;
    })();
    console.log("Game", Game);

    // 鼠标事件封装
    const MouseEvent = (() => {
        var x = y = 0;
        var isDown = false;

        // 按下 移动 抬起 点击
        const TODO = { down: {}, move: {}, up: {}, click: {} };
        const call = (obj, e, ...args) => {
            for (const key in obj)
                obj[key](e.clientX, e.clientY, ...args);
        }

        const down = e => {
            x = e.clientX;
            y = e.clientY;
            isDown = true;
            call(TODO.down, e);
        }

        const move = e => {
            call(TODO.move, e, x, y, isDown);
        }

        const up = e => {
            isDown = false;
            call(TODO.up, e, x, y);
            // 增加识别范围 防止手抖导致错失点击事件
            if (Math.abs(e.clientX - x) + Math.abs(e.clientY - y) < cfg.TouchFT)
                call(TODO.click, e, x, y);
        }

        var tx = 0, ty = 0;
        const trans = fn => {
            return e => {
                const target = e.targetTouches[0]; // 暂时支持单指
                if (target !== undefined) {
                    e.clientX = target.clientX;
                    e.clientY = target.clientY;
                    tx = e.clientX;
                    ty = e.clientY;
                } else {
                    e.clientX = tx;
                    e.clientY = ty;
                }
                fn(e);
            }
        }

        map.addEventListener('mousedown', down);
        map.addEventListener('touchstart', trans(down));
        map.addEventListener('mousemove', move);
        map.addEventListener('touchmove', trans(move));
        map.addEventListener('mouseup', up);
        return TODO;
    })();
    console.log("MouseEvent", MouseEvent);

    draw.drawLine = (x1, y1, x2, y2, w, color) => {
        draw.lineWidth = w;
        draw.strokeStyle = color;
        draw.beginPath();
        draw.moveTo(x1, y1);
        draw.lineTo(x2, y2);
        draw.stroke();
    }

    // 绘制输出
    draw.push = (...args) => {
        foreach(args, obj => {
            if (!obj.visible(obj)) return;

            draw.save();
            draw.globalAlpha = obj.opacity;

            const grd = draw.createRadialGradient(
                obj.x, obj.y, Math.max(obj.w2, obj.h2) / 3,
                obj.x, obj.y, Math.max(obj.w, obj.h)
            );
            grd.addColorStop(0, obj.color);
            grd.addColorStop(1, obj.color2);
            draw.fillStyle = grd;

            if (obj.r) { // 圆形
                draw.beginPath();
                draw.arc(obj.x, obj.y, obj.r / 2, 0, Math.PI * 2, true);
                draw.fill();
            } else draw.fillRect(...obj.rect()); // 矩形

            if (obj.txt) {
                draw.fillStyle = obj.txtColor;
                draw.font = obj.fontSize + "px " + cfg.EnFF;
                draw.fillText(obj.txt, obj.x - obj.fontSize / 3.33 * obj.txt.length,
                    obj.y);
            }

            if (obj.htxt) {
                draw.fillStyle = obj.txtColor;
                draw.font = obj.fontSize + "px " + cfg.CnFF;
                draw.fillText(obj.htxt, obj.x - obj.fontSize / 2.22 * obj.htxt.length,
                    obj.y);
            }

            if (obj.type == "Common")
                obj.update(obj);
            draw.restore();
        });
    }

    // new badblock
    (function newBadblock() {
        // 添加背景
        Game.addCBlock(w / 2, h / 2, w, h, "#ddd"); // 整块背景
        const bg = Game.addCBlock(w / 2, h / 3, w, h, "#eee"); // 上大块背景
        bg.fontSize = vm(50);
        bg.update = obj => {
            draw.strokeStyle = "#ddd";
            var ba = dt() / 10; // 旋转速度
            var ea = ba + 60;
            draw.lineWidth = vm(5);
            draw.beginPath();
            draw.arc(obj.x, obj.y, vm(50), deg2(ba), deg2(ea), false);
            draw.stroke();
            draw.lineWidth = vm(10);
            draw.strokeStyle = "#fff";
            for (let i = 0; i < 4; i++) {
                var a = 90 * i - ba;
                draw.beginPath();
                draw.arc(obj.x, obj.y, vm(50), deg2(a - 30), deg2(a + 30), false);
                draw.stroke();
            }
        }

        const sh = h - bg.vector()[3];
        const sy = bg.vector()[3] - sh / 2;
        const s = Game.addCBlock(w / 2, sy, w, sh, "#ccc"); // 分数板
        s.fontSize = vm(22);

        Game.addCBlock(w / 2, bg.vector()[3], w, 1, "#f00") // 无尽模式红线
            .visible = () => Game.infinity; // 只有无尽模式才显示

        Game.addCBlock(w / 2, vm(15), w, vm(1), "#fff"); // 顶部白线
        Game.addCBlock(vm(15), h / 2, vm(1), h, "#fff"); // 左侧白线
        Game.addCBlock(w - vm(15), h / 2, vm(1), h, "#fff"); // 右侧白线

        // 添加边界
        if (Game.blocks.length === 0) {
            Game.common.push(
                Game.addBlock(w / 2, 0, w, vm(3), -1, 0, "#000"), // 顶部边界
                Game.addBlock(0, h / 2, vm(3), h, -1, 0, "#000"), // 左侧边界
                Game.addBlock(w, h / 2, vm(3), h, -1, 0, "#000") // 右侧边界
            );
            Game.blocks = [];
        }
    })();

    // new roadblock
    function newRoadblock() {
        const b = cfg.bm; // 方块间距
        const c = 8
        var r = c * 2;
        const bw = vw(100 / c) - vm(b),
            bh = vm(100 / r) - vm(b);
        const nr = Math.ceil(h / bh / 3 * 2);
        r = Game.infinity ? 1 : r;
        r = nr > r ? r : nr; // 限制最大方块数量

        for (let y = 0; y < r; y++) {
            for (let x = 0; x < c; x++) {
                if (PR(Game.infinity ? cfg.p.nrbi : cfg.p.nrb)) {
                    const nb = PR(Game.infinity ? cfg.p.nrbinb : cfg.p.nrbnb) ?
                        random(2, cfg.nrbnbmax) : 0;
                    const h = Game.infinity ? 1 : random(1, cfg.nrbhmax);
                    const block = Game.addBlock(
                        x * (bw + vm(b)) + bw / 2,
                        y * (bh + vm(b)) + bh / 2,
                        bw, bh, h, nb);
                    block.opacity = .6; // 半透明方块
                    block.m = bh / 8; // margin-top
                    if (Game.infinity && PR(cfg.p.nrbib)) {
                        block.effectBoom = true;
                        block.newBullet = 0;
                    }

                    if (nb > 0)
                        block.color = cfg.c.bnb;
                    if (block.effectBoom)
                        block.color = cfg.c.eb;

                    block.score = nb > 0 ? 1 : h;
                }
            }
        }
    } newRoadblock();

    // 发射平台逻辑
    function newLauncher() {
        Game.Launcher = new Game.Block(vw(50), vh(90), vm(30), vm(5), -1, 0, cfg.c.l);
        Game.Launcher.bs = Game.infinity ?
            1 : Math.ceil(Game.getBlockCount() * cfg.btn);
        Game.Launcher.txt = "" + Game.Launcher.bs;
        Game.Launcher.effectBoomT = 0;

        // 更新鼠标坐标
        MouseEvent.move.Mouse = (x, y, isDown) => {
            if (Game.paused)
                return;

            [mousex, mousey] = [x, y];
        }

        // 使平台可以自主发射小球
        MouseEvent.up.Launcher = (x, y, dx, dy) => {
            if (Game.paused)
                return;

            // 小球不够不能发射
            if (Game.Launcher.bs < 1)
                return;

            // 新增小球
            const b = Game.addBullet(Game.Launcher.x, Game.Launcher.y,
                random(-cfg.btd, cfg.btd) - 90, vm(100));

            // 新小球位于平台上方
            b.y -= Game.Launcher.h2 + b.h2;
            Game.Launcher.bs--;
            Game.Launcher.txt = "" + Game.Launcher.bs;
            if (Game.Launcher.bs < 1)
                Game.Launcher.txtColor = "#f00";
        }

        // 爆发效果实现
        MouseEvent.move.LauncherEffectBoom = (x, y, isDown) => {
            if (Game.paused)
                return;

            if (Game.Launcher.effectBoomT < dt())
                return;

            // 新增小球
            const b = Game.addBullet(Game.Launcher.x, Game.Launcher.y,
                random(-cfg.btd, cfg.btd) - 90, vm(100));
            // 新小球位于平台上方
            b.y -= Game.Launcher.h2 + b.h2;
            // 小球属于分裂小球
            b.ia = true;
            b.color = cfg.c.btnb;
            // 适配无尽模式速度
            b.addSpeed = cfg.btnbas;
        }
    } newLauncher();

    const options = document.querySelector("#fo");
    const start = document.querySelector("#start");
    const start_infinity = document.querySelector("#start_infinity");
    const restart = document.querySelector("#mr");
    const pause = document.querySelector("#mp");

    const toggleVisibility = (e) => {
        if (e.getAttribute("style")) e.removeAttribute("style");
        else e.setAttribute("style", "display: none;");
    }

    const changePaused = (p) => {
        Game.paused = p;

        pause.innerHTML = Game.paused ? "继续" : "暂停";
        pause.style.backgroundColor = Game.paused ? "yellow" : "";

        dtl = dtr();
    }

    const newGameRound = (isInfinity) => {
        Game.bullets = [];
        Game.blocks = [];
        Game.infinity = isInfinity;
        Game.score = 0;
        Game.common[1].htxt = Game.infinity ? "苟" : "稳";
        Game.common[1].txtColor = "#fff";

        if (!isInfinity) newRoadblock();
        newLauncher();

        changePaused(false);

        toggleVisibility(document.querySelector("#op"));
        toggleVisibility(document.querySelector("#m"));
    }

    // 暂停游戏
    pause.onclick = () => changePaused(!Game.paused);

    // 无尽模式
    const infinityMod = () => {
        Game.infinity_dt = dt();

        foreach(Game.blocks, (block) => {
            if (block.health === -1)
                return;

            block.y += block.h + block.m;
        });

        newRoadblock();
    }

    // 点击开始开始游戏
    start.onclick = restart.onclick = () => newGameRound();
    start_infinity.onclick = () => {
        newGameRound(true);
        Game.infinity_dt = dt();
        infinityMod();
    }

    // running (0_0! )
    function animloop() {
        // 暂停实现
        if (!Game.paused) {
            // 游戏时间变化
            dtt += dtr() - dtl;
            dtl = dtr();

            // 使平台可以被拖动
            if (mousex >= Game.Launcher.w2 && mousex <= w - Game.Launcher.w2)
                Game.Launcher.move(mousex);

            // 无尽模式
            const isFastest = Game.bullets[0] && !Game.bullets[0].ia &&
                Game.bullets[0].addSpeed > cfg.btiasmax * 0.8;
            if (Game.infinity && dt() - Game.infinity_dt > cfg.ids / (
                isFastest ? 2 : 1
            )) {
                // 皮一下
                if (isFastest) {
                    Game.common[1].htxt = "皮";
                    Game.common[1].txtColor = "#ff0";
                } else if (Game.common[1].htxt == "皮") {
                    Game.common[1].htxt = "寄";
                    Game.common[1].txtColor = "#fff";
                }

                infinityMod();
            }

            // 小球主动更新状态
            foreach(Game.bullets, (bullet, i) => {
                bullet.update();
                // 小球超出边界自动移除
                if (!Frontier.rectWith(bullet, Game.common[0]))
                    Game.bullets.splice(i, 1);
            });

            // 更新画面上的游戏实时分数
            Game.common[2].txt = "" + Game.score;

            // 游戏结果判断
            var isEnd;
            if (Game.infinity) {
                if ((Game.Launcher.bs === 0 && Game.getBulletCount() === 0) ||
                    (Game.blocks[0] &&
                        !Frontier.rectWith(Game.blocks[0], Game.common[1])))
                    isEnd = true;
            } else {
                if ((Game.Launcher.bs === 0 && Game.getBulletCount() === 0 &&
                    Game.getBlockCount() > 0) || Game.getBlockCount() === 0)
                    isEnd = true;
            }
            if (isEnd) {
                document.querySelector("#score").innerHTML = Game.score;
                newGameRound();
            }

            draw.clearRect(0, 0, w, h);
            draw.push(...Game.common);
            draw.push(...Game.blocks);
            draw.push(Game.Launcher);
            draw.push(...Game.bullets);

            [ // 左下角信息
                "MousePos: " + mousex.toFixed(2) + " " + mousey.toFixed(2),
                "Block count: " + Game.getBlockCount(),
                "Bullet count: " + Game.getBulletCount(),
                "Game Score: " + Game.score,
            ].map((v, i) => {
                draw.fillText(v, 10, h - 10 - 12 * i);
            });
        }

        // flash
        requestAnimationFrame(animloop);
    } animloop();

    // 初始化设置面板
    [
        ["经典模式"],
        ["(%)方块生成概率", cfg.p.nrb, n => cfg.p.nrb = n],
        ["(%)分裂方块生成概率", cfg.p.nrbnb, n => cfg.p.nrbnb = n],
        ["方块最大撞击次数", cfg.nrbhmax, n => cfg.nrbhmax = n],
        ["小球最大分裂数量", cfg.nrbnbmax, n => cfg.nrbnbmax = n],
        ["小球最快速度", cfg.btasmax, n => cfg.btasmax = n],
        ["小球增速倍数", cfg.btasa, n => cfg.btasa = n],
        ["小球发射角度偏移", cfg.btd, n => cfg.btd = n],
        ["无尽模式"],
        ["(%)方块生成概率", cfg.p.nrbi, n => cfg.p.nrbi = n],
        ["方块下降时间间隔", cfg.ids, n => cfg.ids = n],
        ["小球最快速度", cfg.btiasmax, n => cfg.btiasmax = n],
        ["小球增速倍数", cfg.btiasa, n => cfg.btiasa = n],
    ].map(([n, v, u]) => {
        var t;

        if (u) {
            const lable = document.createElement("label");
            lable.setAttribute("for", n);
            lable.style.whiteSpace = "nowrap";
            lable.innerHTML = n + ": ";

            const input = document.createElement("input");
            input.setAttribute("id", n);
            input.setAttribute("type", "number");
            input.setAttribute("value", v);
            input.onchange = e => u(e.target.value);

            const tdn = document.createElement("td");
            tdn.append(lable);
            const tdv = document.createElement("td");
            tdv.append(input);

            t = document.createElement("tr");
            t.append(tdn);
            t.append(tdv);
        } else {
            t = document.createElement("th");
            t.setAttribute("colspan", "2");
            t.innerHTML = n;
        }

        options.append(t);
    });
}
